﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using BattleOfColors.Control;
using BattleOfColors.Model;


namespace BattleOfColors
{
    public partial class FormMain : Form
    {
        private int dimension;
        private int panelHeight;
        private int panelWidth;
        private int turnNumber;

        private bool exit;
        private bool running;
        private bool pause;

        private Bitmap backBuffer;

        private Graphics _graphics;
        public Graphics GraphicsBackBuffer
        {
            get { return _graphics; }
            private set { _graphics = value; }
        }

        private Graphics _graphicsPanel;
        public Graphics GraphicsPanel
        {
            get { return _graphicsPanel; }
            set { _graphicsPanel = value; }
        }

        private Thread thread;
        private Tile[,] battlefield;

        private List<Soldier> greenTeam;
        private List<Soldier> redTeam;

        public FormMain()
        {
            InitializeComponent();

            SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);

            CheckForIllegalCrossThreadCalls = false;

            backBuffer = new Bitmap(panelDrawning.Width, panelDrawning.Height);
            GraphicsPanel = panelDrawning.CreateGraphics();
            GraphicsBackBuffer = Graphics.FromImage(backBuffer);

            Application.ApplicationExit += new EventHandler(MemoryCleanup);

            panelHeight = panelDrawning.Size.Height;
            panelWidth = panelDrawning.Size.Width;

            dimension = panelHeight / Specification.Instance.TileSize;
            Specification.Instance.Dimension = dimension;

            running = false;

        }

        void MemoryCleanup(object sender, EventArgs e)
        {
            Specification.Instance.Mutex.WaitOne();
            GraphicsPanel.Dispose();
            GraphicsBackBuffer.Dispose();

            backBuffer.Dispose();

            exit = true;
            thread.Abort();
            Specification.Instance.Mutex.Release();
        }

        private void Battle()
        {
            greenTeam = new List<Soldier>();
            redTeam = new List<Soldier>();

            int numberOfGreenSoldiers = 60; // Specification.Instance.NumberOfSoldiers;
            int numberOfRedSoldiers =   60; // Specification.Instance.NumberOfSoldiers;

            //Gerando os soldados para o campo de batalha
            for (int i = 0; i < numberOfGreenSoldiers; i++)//Specification.Instance.NumberOfSoldiers; i++)
            {
                greenTeam.Add(Soldier.CreateSoldier(Model.Color.GREEN));
                
            }

            for (int i = 0; i < numberOfRedSoldiers; i++)
            {
                redTeam.Add(Soldier.CreateSoldier(Model.Color.RED));
            }

            //Geração do campo de batalha, uma matriz de blocos
            battlefield = new Tile[dimension, dimension];

            //Tanto faz se for no número de verdes quanto o número de vermelhos, a qtde é igual
            int soldiersStartPosition = (int)((float)dimension / 2) - (int)((float)numberOfGreenSoldiers / 2);

            //Inicialização dos tiles do campo de batalha
            for (int i = 0; i < dimension; i++) //i é o X
            {
                for (int j = 0; j < dimension; j++) //j é o Y
                {
                    battlefield[i, j] = new Tile(i*10, j*10, i, j);

                    //Se for na segunda linha, escolha default para posicionar os soldados vermelhos
                    //E se ainda não foram colocados todos os soldados vermelhos
                    //E se já está, pelo menos, na posição X inicial de posicionamento
                    if (i == 1 && numberOfRedSoldiers > 0 && j >= soldiersStartPosition) 
                    {
                        //Adicionado os soldados na ordem inversa da lista
                        Soldier redSoldier = redTeam[numberOfRedSoldiers - 1];
                        battlefield[i, j].PositionedSoldier = redSoldier;
                        redSoldier.Location = battlefield[i, j];
                        //Seta os valores de X e Y do soldado para a posição do tile
                        redSoldier.X = battlefield[i, j].X;
                        redSoldier.Y = battlefield[i, j].Y;

                        numberOfRedSoldiers--;
                    }

                    //Se for na penúltima linha, escolha default para posicionar os soldados verdes
                    //E se ainda não foram colocados todos os soldados verdes
                    //E se já está, pelo menos, na posição X inicial de posicionamento
                    if (i == dimension - 2 && numberOfGreenSoldiers > 0 && j >= soldiersStartPosition)
                    {
                        //Adicionado os soldados na ordem inversa da lista
                        Soldier greenSoldier = greenTeam[numberOfGreenSoldiers - 1];
                        battlefield[i, j].PositionedSoldier = greenSoldier;
                        greenSoldier.Location = battlefield[i, j];
                        //Seta os valores de X e Y do soldado para a posição do tile
                        greenSoldier.X = battlefield[i, j].X;
                        greenSoldier.Y = battlefield[i, j].Y;

                        numberOfGreenSoldiers--;
                    }
                }
            }

            Specification.Instance.Battlefield = battlefield;
            turnNumber = 0;
            while (!exit)
            {
                if (!pause)
                {
                    Specification.Instance.Mutex.WaitOne();
                    Draw();
                    UpdateBattleField();
                    turnNumber++;
                    Thread.Sleep(Specification.Instance.SleepTime);
                    Specification.Instance.Mutex.Release();
                }
            }
        }

        private void Draw()
        {
            int size = Specification.Instance.TileSize;

            SolidBrush brush = null;
            brush = new SolidBrush(System.Drawing.Color.Black);
            GraphicsBackBuffer.FillRectangle(brush, 0, 0, panelHeight, panelWidth);
            brush.Dispose();

            for (int i = 0; i < greenTeam.Count; i++)
            {
                brush = new SolidBrush(System.Drawing.Color.Green);
                Soldier greenSoldier = greenTeam[i];
                if (greenSoldier.Attacked)
                {
                    brush = new SolidBrush(System.Drawing.Color.BlueViolet);
                    greenSoldier.Attacked = false;
                }
                GraphicsBackBuffer.FillRectangle(brush, (float)greenSoldier.X, (float)greenSoldier.Y, size, size);
            }

            for (int i = 0; i < redTeam.Count; i++)
            {
                brush = new SolidBrush(System.Drawing.Color.Red);
                Soldier redSoldier = redTeam[i];
                if (redSoldier.Attacked)
                {
                    brush = new SolidBrush(System.Drawing.Color.BlueViolet);
                    redSoldier.Attacked = false;
                }
                GraphicsBackBuffer.FillRectangle(brush, (float)redSoldier.X, (float)redSoldier.Y, size, size);
            }

            for (int i = 0; i < Specification.Instance.Dimension; i++)
            {
                for (int j = 0; j < Specification.Instance.Dimension; j++)
                {
                    Tile tile = Specification.Instance.Battlefield[i, j];
                    if (tile.PositionedSoldier == null && tile.HaveAnySmell())
                    {
                        System.Drawing.Color color = Smell.GetSmellColor(tile.Smell.Type);
                        brush = new SolidBrush(color);
                        GraphicsBackBuffer.FillRectangle(brush, (float)tile.X, (float)tile.Y, size, size);
                    }
                }
            }

            brush.Dispose();

            GraphicsPanel.DrawImageUnscaled(backBuffer, 0, 0);
        }

        private void UpdateBattleField()
        {
            List<Soldier> newGreenTeam = new List<Soldier>();
            List<Soldier> newRedTeam = new List<Soldier>();

            greenTeam.Sort();
            redTeam.Sort();

            for (int i = 0; i < Specification.Instance.NumberOfSoldiers; i++)
            {
                if (i < greenTeam.Count)
                {
                    greenTeam[i].Update();
                }

                if (i < redTeam.Count)
                {
                    redTeam[i].Update();
                }

                if (i < greenTeam.Count && greenTeam[i].Life > 0)
                {
                    newGreenTeam.Add(greenTeam[i]);
                }

                if (i < redTeam.Count && redTeam[i].Life > 0)
                {
                    newRedTeam.Add(redTeam[i]);
                }
            }

            greenTeam = newGreenTeam;
            redTeam = newRedTeam;

            for (int i = 0; i < dimension; i++)
            {
                for (int j = 0; j < dimension; j++)
                {
                    battlefield[i, j].Update();
                }
            }

            textBoxGreenSoldiers.Text = greenTeam.Count + "";
            textBoxRedSoldiers.Text = redTeam.Count + "";
            textBoxTurn.Text = turnNumber + "";
             
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            if (!running)
            {
                running = true;
                exit = false;
                pause = false;
                if (thread != null)
                {
                    thread.Abort();
                }
                thread = new Thread(new ThreadStart(this.Battle));
                thread.Start();

                buttonStart.Text = "Stop";
            }
            else
            {
                running = false;
                exit = true;
                buttonStart.Text = "Start";
            }
        }

        private void buttonPause_Click(object sender, EventArgs e)
        {
            pause = !pause;
        }
    }
}
